/////////////////////////////////////////////////////////////////////////////
// Name:        shortcutcust.cpp
// Purpose:     Classes for allowing the user to customise shortcuts.
// Author:      Julian Smart
// Modified by:
// Created:     2009-04-13
// RCS-ID:      $Id$
// Copyright:   (c) Julian Smart
// Licence:     New BSD License
/////////////////////////////////////////////////////////////////////////////

#include "wx/wx.h"

#include "shortcutcust/shortcutcust.h"

#include "wx/arrimpl.cpp"

WX_DEFINE_OBJARRAY(wxShortcutItemArray)

static const struct wxShortcutKeyName
{
    wxKeyCode code;
    const wxChar *name;
} wxShortcutKeyNames[] =
{
    { WXK_DELETE, wxTRANSLATE("DEL") },
    { WXK_DELETE, wxTRANSLATE("DELETE") },
    { WXK_BACK, wxTRANSLATE("BACK") },
    { WXK_INSERT, wxTRANSLATE("INS") },
    { WXK_INSERT, wxTRANSLATE("INSERT") },
    { WXK_RETURN, wxTRANSLATE("ENTER") },
    { WXK_RETURN, wxTRANSLATE("RETURN") },
    { WXK_PAGEUP, wxTRANSLATE("PGUP") },
    { WXK_PAGEDOWN, wxTRANSLATE("PGDN") },
    { WXK_LEFT, wxTRANSLATE("LEFT") },
    { WXK_RIGHT, wxTRANSLATE("RIGHT") },
    { WXK_UP, wxTRANSLATE("UP") },
    { WXK_DOWN, wxTRANSLATE("DOWN") },
    { WXK_HOME, wxTRANSLATE("HOME") },
    { WXK_END, wxTRANSLATE("END") },
    { WXK_SPACE, wxTRANSLATE("SPACE") },
    { WXK_TAB, wxTRANSLATE("TAB") },
    { WXK_ESCAPE, wxTRANSLATE("ESC") },
    { WXK_ESCAPE, wxTRANSLATE("ESCAPE") },
    { WXK_CANCEL, wxTRANSLATE("CANCEL") },
    { WXK_CLEAR, wxTRANSLATE("CLEAR") },
    { WXK_MENU, wxTRANSLATE("MENU") },
    { WXK_PAUSE, wxTRANSLATE("PAUSE") },
    { WXK_CAPITAL, wxTRANSLATE("CAPITAL") },
    { WXK_SELECT, wxTRANSLATE("SELECT") },
    { WXK_PRINT, wxTRANSLATE("PRINT") },
    { WXK_EXECUTE, wxTRANSLATE("EXECUTE") },
    { WXK_SNAPSHOT, wxTRANSLATE("SNAPSHOT") },
    { WXK_HELP, wxTRANSLATE("HELP") },
    { WXK_ADD, wxTRANSLATE("ADD") },
    { WXK_SEPARATOR, wxTRANSLATE("SEPARATOR") },
    { WXK_SUBTRACT, wxTRANSLATE("SUBTRACT") },
    { WXK_DECIMAL, wxTRANSLATE("DECIMAL") },
    { WXK_DIVIDE, wxTRANSLATE("DIVIDE") },
    { WXK_NUMLOCK, wxTRANSLATE("NUM_LOCK") },
    { WXK_SCROLL, wxTRANSLATE("SCROLL_LOCK") },
    { WXK_PAGEUP, wxTRANSLATE("PAGEUP") },
    { WXK_PAGEDOWN, wxTRANSLATE("PAGEDOWN") },
    { WXK_NUMPAD_SPACE, wxTRANSLATE("KP_SPACE") },
    { WXK_NUMPAD_TAB, wxTRANSLATE("KP_TAB") },
    { WXK_NUMPAD_ENTER, wxTRANSLATE("KP_ENTER") },
    { WXK_NUMPAD_HOME, wxTRANSLATE("KP_HOME") },
    { WXK_NUMPAD_LEFT, wxTRANSLATE("KP_LEFT") },
    { WXK_NUMPAD_UP, wxTRANSLATE("KP_UP") },
    { WXK_NUMPAD_RIGHT, wxTRANSLATE("KP_RIGHT") },
    { WXK_NUMPAD_DOWN, wxTRANSLATE("KP_DOWN") },
    { WXK_NUMPAD_PAGEUP, wxTRANSLATE("KP_PRIOR") },
    { WXK_NUMPAD_PAGEUP, wxTRANSLATE("KP_PAGEUP") },
    { WXK_NUMPAD_PAGEDOWN, wxTRANSLATE("KP_NEXT") },
    { WXK_NUMPAD_PAGEDOWN, wxTRANSLATE("KP_PAGEDOWN") },
    { WXK_NUMPAD_END, wxTRANSLATE("KP_END") },
    { WXK_NUMPAD_BEGIN, wxTRANSLATE("KP_BEGIN") },
    { WXK_NUMPAD_INSERT, wxTRANSLATE("KP_INSERT") },
    { WXK_NUMPAD_DELETE, wxTRANSLATE("KP_DELETE") },
    { WXK_NUMPAD_EQUAL, wxTRANSLATE("KP_EQUAL") },
    { WXK_NUMPAD_MULTIPLY, wxTRANSLATE("KP_MULTIPLY") },
    { WXK_NUMPAD_ADD, wxTRANSLATE("KP_ADD") },
    { WXK_NUMPAD_SEPARATOR, wxTRANSLATE("KP_SEPARATOR") },
    { WXK_NUMPAD_SUBTRACT, wxTRANSLATE("KP_SUBTRACT") },
    { WXK_NUMPAD_DECIMAL, wxTRANSLATE("KP_DECIMAL") },
    { WXK_NUMPAD_DIVIDE, wxTRANSLATE("KP_DIVIDE") },
    { WXK_WINDOWS_LEFT, wxTRANSLATE("WINDOWS_LEFT") },
    { WXK_WINDOWS_RIGHT, wxTRANSLATE("WINDOWS_RIGHT") },
    { WXK_WINDOWS_MENU, wxTRANSLATE("WINDOWS_MENU") },
    { WXK_COMMAND, wxTRANSLATE("COMMAND") },
};

void wxShortcutItem::Init()
{
    m_id = 0;
    m_enabled = true;
    m_flags = 0;
    m_keyCode = 0;
}

void wxShortcutItem::Copy(const wxShortcutItem& item, bool excludeId)
{
    if (!excludeId)
        m_id = item.m_id;

    m_enabled = item.m_enabled;
    m_name = item.m_name;
    m_category = item.m_category;
    m_description = item.m_description;
    m_flags = item.m_flags;
    m_keyCode = item.m_keyCode;
}

bool wxShortcutItem::operator==(const wxShortcutItem& item) const
{
    return (
    //m_id == item.m_id &&
    m_enabled == item.m_enabled &&
    m_name == item.m_name &&
    m_category == item.m_category &&
    m_description == item.m_description &&
    m_flags == item.m_flags &&
    m_keyCode == item.m_keyCode
        );
}

// Copies the shortcuts
void wxShortcutManager::Copy(const wxShortcutManager& manager)
{
    m_shortcuts = manager.m_shortcuts;
}

// Initialises the class
void wxShortcutManager::Init()
{
}

// Clears the items
void wxShortcutManager::Clear()
{
    m_shortcuts.Clear();
}

// Adds information about a shortcut
void wxShortcutManager::Add(const wxShortcutItem& info)
{
    m_shortcuts.Add(info);
}

// Finds information about a shortcut
wxShortcutItem* wxShortcutManager::Find(const wxString& name) const
{
    size_t i;
    for (i = 0; i < m_shortcuts.GetCount(); i++)
    {
        wxShortcutItem* item = & m_shortcuts[i];
        if (item->GetName() == name)
            return item;
    }
    return NULL;
}

// Finds the nth info for the given name
int wxShortcutManager::FindIndex(const wxString& name) const
{
    size_t i;
    for (i = 0; i < m_shortcuts.GetCount(); i++)
    {
        wxShortcutItem* item = & m_shortcuts[i];
        if (item->GetName() == name)
            return i;
    }
    return -1;
}

// Gets the nth record
wxShortcutItem* wxShortcutManager::GetNth(int n) const
{
    if (n >= 0 && n < (int) m_shortcuts.GetCount())
        return & m_shortcuts[n];
    else
        return NULL;
}

// Saves customisation to config
bool wxShortcutManager::Save(wxConfigBase& config)
{
    size_t i;
    for (i = 0; i < m_shortcuts.GetCount(); i++)
    {
        wxShortcutItem& item = m_shortcuts[i];
        config.Write(wxT("Shortcuts/") + item.GetName() + wxT("-Enabled"), item.IsEnabled());
        // Don't want to save identifier since it could change between invocations of the application.
        // config.Write(wxT("Shortcuts/") + item.GetName() + wxT("-Id"), item.GetId());
        config.Write(wxT("Shortcuts/") + item.GetName() + wxT("-Description"), item.GetDescription());
        config.Write(wxT("Shortcuts/") + item.GetName() + wxT("-Flags"), item.GetFlags());
        config.Write(wxT("Shortcuts/") + item.GetName() + wxT("-KeyCode"), item.GetKeyCode());
    }
    return true;
}

// Save current state to original shortcut store
bool wxShortcutManager::SaveOriginalShortcuts()
{
    m_originalShortcuts = m_shortcuts;
    return true;
}

/// Restore original shortcuts
bool wxShortcutManager::ResetShortcuts()
{
    m_shortcuts = m_originalShortcuts;
    return true;
}

// Load customisation from config
bool wxShortcutManager::Load(wxConfigBase& config)
{
    SaveOriginalShortcuts();

    size_t i;
    for (i = 0; i < m_shortcuts.GetCount(); i++)
    {
        wxShortcutItem& item = m_shortcuts[i];
        config.Read(wxT("Shortcuts/") + item.GetName() + wxT("-Enabled"), & item.m_enabled);
        // Don't want to load identifier since it could change between invocations of the application.
        // config.Read(wxT("Shortcuts/") + item.GetName() + wxT("-Id"), & item.m_id);
        config.Read(wxT("Shortcuts/") + item.GetName() + wxT("-Description"), & item.m_description);
        config.Read(wxT("Shortcuts/") + item.GetName() + wxT("-Flags"), & item.m_flags);
        config.Read(wxT("Shortcuts/") + item.GetName() + wxT("-KeyCode"), & item.m_keyCode);
    }
    return false;
}

// Make an accelerator table
wxAcceleratorTable wxShortcutManager::MakeAcceleratorTable()
{
    int count = 0;
    size_t i;
    for (i = 0; i < m_shortcuts.GetCount(); i++)
    {
        wxShortcutItem& item = m_shortcuts[i];
        if (item.IsEnabled())
            count ++;
    }

    if (count == 0)
        return wxAcceleratorTable();

    wxAcceleratorEntry* entries = new wxAcceleratorEntry[count];
    count = 0;
    for (i = 0; i < m_shortcuts.GetCount(); i++)
    {
        wxShortcutItem& item = m_shortcuts[i];
        if (item.IsEnabled())
        {
            entries[count].Set(item.GetFlags(), item.GetKeyCode(), item.GetId());
            count ++;
        }
    }

    wxAcceleratorTable table(count, entries);
    delete[] entries;
    return table;
}

// Makes and sets the accelerator table into a particular window
bool wxShortcutManager::SetAcceleratorTable(wxWindow* window)
{
    wxAcceleratorTable table = MakeAcceleratorTable();
    window->SetAcceleratorTable(table);

    return true;
}

// Returns a human-readable string for the shortcut
wxString wxShortcutManager::GetReadableShortcutString(const wxShortcutItem& item) const
{
    wxAcceleratorEntry accel(item.GetFlags(), item.GetKeyCode(), item.GetId());
    wxString str = accel.ToString();

    str.Replace(_("Alt-"), _("Alt + "));
    str.Replace(_("Ctrl-"), _("Control + "));
    str.Replace(_("Shift-"), _("Shift + "));
    str.Replace(_("XCtrl-"), _("Control + "));

    return str;
}
    
// Returns a shortcut string suitable for adding to menu item.
wxString wxShortcutManager::GetMenuShortcutString(const wxShortcutItem& item) const
{
    wxAcceleratorEntry accel(item.GetFlags(), item.GetKeyCode(), item.GetId());
    wxString str = accel.ToString();
    // Windows uses - to separate items in a shortcut, but + is more common
    if (str.Find(wxT("--")) != wxNOT_FOUND)
        str.Replace(wxT("--"), wxT("+-"));
    else
        str.Replace(wxT("-"), wxT("+"));
    return str;
}   

// Is this a valid shortcut keycode?
bool wxShortcutManager::IsValidShortcutKeyCode(int keycode)
{
    if ( keycode >= WXK_F1 && keycode <= WXK_F12 )
        return true;
    else if ( keycode >= WXK_NUMPAD0 && keycode <= WXK_NUMPAD9 )
        return true;
    else if ( keycode >= WXK_SPECIAL1 && keycode <= WXK_SPECIAL20 )
        return true;
    else // check the named keys
    {
        size_t n;
        for ( n = 0; n < WXSIZEOF(wxShortcutKeyNames); n++ )
        {
            const wxShortcutKeyName& kn = wxShortcutKeyNames[n];
            if ( keycode == kn.code )
            {
                return true;
            }
        }

        // must be a simple key
        if (
#if !wxUSE_UNICODE
            isascii(keycode) &&
#endif // ANSI
            wxIsalnum(keycode) )
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

// Return an array of all the shortcut items that have the given shortcut,
// except idException (if not -1).
wxShortcutItemArray wxShortcutManager::GetMatchingShortcuts(const wxShortcutItem& item, int idException) const
{
    wxShortcutItemArray arr;
    size_t i;
    for (i = 0; i < m_shortcuts.GetCount(); i++)
    {
        wxShortcutItem& it = m_shortcuts[i];
#if 0
        wxLogDebug(wxT("We need: keycode=%d, flags=%d. We have: keycode=%d, flags=%d."),
            (int) item.GetKeyCode(), item.GetFlags(), (int) it.GetKeyCode(), it.GetFlags());
#endif

        if ((idException == -1 || it.GetId() != idException) &&
            (it.GetKeyCode() == item.GetKeyCode() && it.GetFlags() == item.GetFlags()))
        {
            arr.Add(it);
        }
    }
    return arr;
}

/// Finds a shortcut by id
wxShortcutItem* wxShortcutManager::FindById(int id) const
{
    size_t i;
    for (i = 0; i < m_shortcuts.GetCount(); i++)
    {
        wxShortcutItem& it = m_shortcuts[i];
        if (it.GetId() == id)
            return & it;
    }
    return NULL;
}

/// Updates labels with new accelerators
bool wxShortcutManager::UpdateMenuBar(wxMenuBar* menuBar)
{
    size_t i;
    for (i = 0; i < menuBar->GetMenuCount(); i++)
    {
        wxMenu* menu = menuBar->GetMenu(i);
        UpdateMenu(menu);
    }
    return true;
}

bool wxShortcutManager::UpdateMenu(wxMenu* menu)
{
    size_t i;
    for (i = 0; i < menu->GetMenuItemCount(); i++)
    {
        wxMenuItem* item = menu->FindItemByPosition(i);
        int id = item->GetId();
        wxShortcutItem* shortcut = FindById(id);
        if (shortcut)
        {
            wxString originalLabel = item->GetText().BeforeFirst(wxT('\t'));
            wxString accelLabel = GetMenuShortcutString(* shortcut);
            if (!accelLabel.IsEmpty())
            {
                wxString label = originalLabel + wxT("\t") + accelLabel;
                menu->SetLabel(id, label);
            }
        }
        if (item->GetSubMenu())
            UpdateMenu(item->GetSubMenu());
    }
    return true;
}

IMPLEMENT_DYNAMIC_CLASS(wxShortcutCtrl, wxTextCtrl)

BEGIN_EVENT_TABLE(wxShortcutCtrl, wxTextCtrl)
    EVT_KEY_DOWN(wxShortcutCtrl::OnKeyDown)
END_EVENT_TABLE()

bool wxShortcutCtrl::Create(wxWindow* parent, wxWindowID id, const wxString& value, const wxPoint& pos,
        const wxSize& size, int style)
{
    return wxTextCtrl::Create(parent, id, value, pos, size, style);
}

void wxShortcutCtrl::Init()
{
    m_shortcut.SetKeyCode((int) 'A');
}

void wxShortcutCtrl::OnKeyDown(wxKeyEvent& event)
{
    if (event.GetKeyCode() == WXK_RETURN || event.GetKeyCode() == WXK_TAB /* || event.GetKeyCode() == WXK_ALT */)
    {
        event.Skip();
        return;
    }

    if (event.GetKeyCode() == WXK_SHIFT)
    {
        if (m_shortcut.GetFlags() & wxACCEL_SHIFT)
            m_shortcut.SetFlags(m_shortcut.GetFlags() & ~wxACCEL_SHIFT);
        else
            m_shortcut.SetFlags(m_shortcut.GetFlags() | wxACCEL_SHIFT);
    }
    else if (event.GetKeyCode() == WXK_ALT)
    {
        if (m_shortcut.GetFlags() & wxACCEL_ALT)
            m_shortcut.SetFlags(m_shortcut.GetFlags() & ~wxACCEL_ALT);
        else
            m_shortcut.SetFlags(m_shortcut.GetFlags() | wxACCEL_ALT);
    }
    else if (event.GetKeyCode() == WXK_CONTROL)
    {
        if (m_shortcut.GetFlags() & wxACCEL_CTRL)
            m_shortcut.SetFlags(m_shortcut.GetFlags() & ~wxACCEL_CTRL);
        else
            m_shortcut.SetFlags(m_shortcut.GetFlags() | wxACCEL_CTRL);
    }
    else
    {
        if (wxShortcutManager::IsValidShortcutKeyCode(event.GetKeyCode()))
            m_shortcut.SetKeyCode(event.GetKeyCode());
    }

    ApplyShortcut();

    SetFocus();
}

void wxShortcutCtrl::ApplyShortcut()
{
    SetValue(GetShortcutString());
}

wxString wxShortcutCtrl::GetShortcutString() const
{
    wxAcceleratorEntry accel(m_shortcut.GetFlags(), m_shortcut.GetKeyCode(), m_shortcut.GetId());
    wxString str = accel.ToString();

    str.Replace(_("Alt-"), _("Alt + "));
    str.Replace(_("Ctrl-"), _("Control + "));
    str.Replace(_("Shift-"), _("Shift + "));
    str.Replace(_("XCtrl-"), _("Control + "));

#if 0
    if (m_shortcut.GetFlags() & wxACCEL_SHIFT)
        str += wxT("Shift + ");
    if (m_shortcut.GetFlags() & wxACCEL_CTRL)
        str += wxT("Ctrl + ");
    if (m_shortcut.GetFlags() & wxACCEL_ALT)
        str += wxT("Alt + ");

    str += (wxChar) m_shortcut.GetKeyCode();
#endif

    return str;
}

